Skip to content

test: add Anthropic Messages recording mode + gateway-tool cassettes#123

Merged
maralbahari merged 1 commit into
vllm-project:mainfrom
ashwing:feat/messages-cassette-recorder
Jul 16, 2026
Merged

test: add Anthropic Messages recording mode + gateway-tool cassettes#123
maralbahari merged 1 commit into
vllm-project:mainfrom
ashwing:feat/messages-cassette-recorder

Conversation

@ashwing

@ashwing ashwing commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Part of #115 (Claude Code — Stage 2). Adds a messages mode to the cassette recorder for the Anthropic Messages API (/v1/messages), and records the upstream gateway-tool traffic that the Claude Code server-side tool loop will replay in tests.

The Stage 2 server-side loop itself is gated on making the executor loop API-agnostic (it's currently typed to Responses RequestPayload/ResponsePayload) — that's a core refactor for a separate change. Recording the acceptance cassette has no such dependency and is the unblocked prerequisite #115 calls out, so this lands the recorder + fixtures ahead of the loop.

What's here:

  • messages recorder mode (record_cassette.py): sends Anthropic /v1/messages with tools, stateless (resends full messages history each turn). With --tool-outputs, a turn following a tool_use feeds back matching tool_result blocks keyed by tool name — mirroring how a gateway tool loop feeds results back. Streaming reconstructs the final message from the Anthropic SSE (content_block_delta / input_json_delta / message_delta).
  • Recorded cassettes (messages/): a two-turn web_search gateway-tool session — model emits the gateway tool_use, tool_result is fed back, model produces the final answer. Both streaming and non-streaming.
  • Reproducer record_messages_cassettes.sh + a README modes-table row.

Test Plan

  • Recorded against vLLM 0.25.1 (the current release, which serves /v1/messages natively), Qwen/Qwen3-30B-A3B-FP8, hermes tool parser + qwen3 reasoning parser.
  • python -m py_compile record_cassette.py clean; --help shows the new mode; cargo build --workspace unaffected (no Rust changed).
  • Recorded cassettes verified: 2 turns each, POST /v1/messages, turn 1 emits tool_use(web_search) with stop_reason: tool_use, turn 2 request carries the full Anthropic history (userassistant with tool_useuser with tool_result), turn 2 produces the final end_turn answer.
  • No Rust consumer yet: the Messages cassette loader lands with the Stage 2 loop these fixtures are for. Recording them now (per [Claude Code - Stage 2] Server-side gateway tool loop for Messages #115's carved-out sub-task) de-risks that work and keeps the fixtures reproducible.
  • Note: streaming reasoning arrives as <think>-tagged text deltas while non-streaming arrives as thinking blocks — this is an accurate capture of what vLLM emits on each path, recorded intentionally so the future parser handles both.

Adds a `messages` mode to the cassette recorder for the Anthropic Messages
API (/v1/messages), and records the upstream gateway-tool traffic that the
Claude Code server-side tool loop (vllm-project#115) will replay in tests.

The mode is stateless (resends full `messages` history each turn); with
`--tool-outputs`, a turn following a `tool_use` feeds back matching
`tool_result` blocks keyed by tool name, mirroring how a gateway tool loop
feeds results back. Streaming reconstructs the final message from the
Anthropic SSE (content_block_delta / input_json_delta / message_delta).

Recorded against vLLM 0.25.1 (Qwen3-30B-A3B-FP8, hermes parser) serving
/v1/messages natively — a two-turn web_search turn: model emits the gateway
tool_use, tool_result is fed back, model answers. Both streaming and
non-streaming. Reproducer: record_messages_cassettes.sh.

These are fixtures for the (maintainer-gated) vllm-project#115 loop; there is no Rust
consumer yet, since the Messages cassette loader lands with that loop.

Part of vllm-project#115.

Signed-off-by: Ashwin Giridharan <girida@amazon.com>

@maralbahari maralbahari left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashwing thanks. just to check my understanding from about the flow. So this PR is only adding cassettes file recorded from vllm upstream v1/messages entrypoint then in a follow up PR would add in the request/response payload mapping on gateway for messages api?
then in another PR handling the SSE chunk mapping?

@ashwing

ashwing commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Yes, exactly. This PR is just the recorder messages mode + the recorded upstream /v1/messages cassettes — no gateway behavior. The follow-up (Stage 2, #115) adds the actual server-side path: Messages request/response payload mapping into the shared tool loop, plus the gateway-owned tool execution that replays against these cassettes.

SSE/streaming chunk mapping I'd keep as its own piece rather than fold into #115 — the streaming cassette here already captures the Anthropic content_block_delta / input_json_delta frames, so it can be tested independently. So roughly: (1) this — fixtures; (2) #115 — request/response mapping + server-side tool loop; (3) streaming event mapping. The exact split of (2)/(3) depends on how the loop's API-agnostic factoring lands, which is still open.

@ashwing

ashwing commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

@maralbahari @franciscojavierarceo can we merge this to unblock the loop changes?

@maralbahari
maralbahari merged commit e108259 into vllm-project:main Jul 16, 2026
3 checks passed
ashwing added a commit to ashwing/agentic-api that referenced this pull request Jul 17, 2026
When an Anthropic Messages request declares a gateway-owned tool (web_search),
run the server-side gateway tool loop natively against vLLM /v1/messages —
execute the tool, hide it from the client, and surface only the final
assistant message. Requests without a gateway-owned tool keep the transparent
proxy (vllm-project#99).

The loop talks Anthropic Messages end to end (no RequestPayload/ResponsePayload
detour), so the client's request is forwarded to vLLM untouched and every
Anthropic field is preserved. It reuses only the protocol-neutral tool layer
(ToolRegistry::dispatch, name-based classification, 60s per-call timeout,
concurrent execution) via a small tool seam.

- types/messages: Anthropic wire types + tool seam (tool_use<->FunctionToolCall,
  ToolOutput->tool_result, gateway/client classification).
- executor::messages_loop: non-streaming loop (hide-the-call, feed tool_result
  back, round cap; client-owned tool_use returns to the client).
- executor::messages_stream: streaming loop + MessagesStreamAccumulator that
  presents one logical message across rounds (single lifecycle, contiguous
  block indices, gateway tool_use suppressed) — the Anthropic-native analogue
  of the Responses GatewayAccumulator (vllm-project#119).
- handler: routes gateway-tool requests to the loop, forwards the client's
  x-api-key, returns the Anthropic error envelope on failure.

Tests: tool-seam + accumulator unit tests, non-streaming and streaming
acceptance tests replaying the recorded vllm-project#123 /v1/messages cassettes, and
handler routing tests. Validated live on G6e (Qwen3 + real You.com):
non-streaming, streaming, mixed client+gateway tools, and proxy fallthrough.

Implements vllm-project#115.

Signed-off-by: Ashwin Giridharan <girida@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants